using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.PipeLine
{

    public class StageRegister : Component
    {

        public NodeVector InputData;
        public NodeVector OutputData;

        public ResetReg Data;
        public PipelineStage PipeSync;

        ClockComponent oClock;
        int iWidth;
        NodeVector nvReset;

        public List<StageRegister> DownstreamStages;

        public StageRegister(Component poParentComponent, string psName, int piWidth, ClockComponent poClock, NodeVector pnvReset)
            : base(poParentComponent, psName)
        {
            iWidth = piWidth;
            oClock = poClock;
            nvReset = pnvReset;
            DownstreamStages = new List<StageRegister>();
        }

        public void LinkDownstreamStage(StageRegister poDownstreamStage)
        {
            DownstreamStages.Add(poDownstreamStage);
        }

        public override void GenerateStructure()
        {
            Data = new ResetReg(this, "data_reg", oClock, iWidth, true, true, false);
            PipeSync = new PipelineStage(this, "pipe_sync", oClock, nvReset,iWidth);

            Data.InputNodes.Join(InputData);
            Data.OutputNodes.Join(OutputData);
            Data.EnableNodes.Join(PipeSync.OutputStall);
            Data.ResetIn.Join(nvReset);
            Data.ResetTo.Join(0);

            if (DownstreamStages.Count == 0)
                throw new Exception("No downstream stages were defined for stage " + this.Name);

            if (DownstreamStages.Count == 1)
            {
                StageRegister oStage = DownstreamStages[0];
                PipeSync.InputStall.Join(oStage.PipeSync.OutputStall);
                PipeSync.OutputDataReady.Join(oStage.PipeSync.InputDataReady);
            }
            else
            {
                OrGate oOrStall = new OrGate(this, "or_stall", DownstreamStages.Count);
                PipeSync.InputStall.Join(oOrStall.OutputNodes);
                int iIdx = 0;
                foreach (StageRegister oStage in DownstreamStages)
                {
                    iIdx++;
                    oOrStall.InputNodes[iIdx] = oStage.PipeSync.InputStall[0];
                    PipeSync.OutputDataReady.Join(oStage.PipeSync.InputDataReady);
                }
            }
        }
    }
}
